package com.qf.shiro2302.config;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.qf.shiro2302.entity.User;
import com.qf.shiro2302.service.UserService;
import com.qf.shiro2302.utils.Constants;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.SimpleAuthenticationInfo;
import org.apache.shiro.authc.credential.HashedCredentialsMatcher;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.realm.Realm;
import org.apache.shiro.spring.web.config.DefaultShiroFilterChainDefinition;
import org.apache.shiro.spring.web.config.ShiroFilterChainDefinition;
import org.apache.shiro.subject.PrincipalCollection;
import org.apache.shiro.util.ByteSource;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import java.util.Arrays;
import java.util.List;

/**
 * shiro的配置类
 */
@Configuration
public class ShiroConfig {

    @Autowired
    private UserService userService;

    @Bean
    public ShiroFilterChainDefinition shiroFilterChainDefinition() {
        DefaultShiroFilterChainDefinition chainDefinition = new DefaultShiroFilterChainDefinition();

        // logged in users with the 'test' role
//        chainDefinition.addPathDefinition("/test/**", "authc, roles[test,admin], perms[document:read,document:write]");

        // 让登录接口直接被shiro的过滤器放行
        // 第一个参数： 接口的路径
        // 第二个参数： shiro内部的过滤器的名字，anon代表无需登录即可访问的特殊的过滤器，注意 过滤器的名字是固定的，不能乱写
        chainDefinition.addPathDefinition("/login/dologin", "anon");
        chainDefinition.addPathDefinition("/login/do", "anon");

        // 放行注册页面
        chainDefinition.addPathDefinition("/register.html", "anon");
        // 放行注册接口
        chainDefinition.addPathDefinition("/user/register", "anon");

        // 放行springboot的错误页面的请求url
        chainDefinition.addPathDefinition("/error", "anon");

        // all other paths require a logged in user
        chainDefinition.addPathDefinition("/**", "authc");
        return chainDefinition;
    }


    @Bean
    public Realm realm(){

        AuthorizingRealm authorizingRealm = new AuthorizingRealm() {

            // 获取身份信息

            /**
             *
             * @param token 这的token就是调用login方法时，传入的token对象
             * @return AuthenticationInfo 对象中，封装了用户的身份信息（principals），密码（credentials），提供信息的realm的名字
             * @throws AuthenticationException
             */
            @Override
            protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
                System.out.println("===============获取身份信息==============");

                // 查询数据库获取当前用户名对应的user对象
                String username = (String) token.getPrincipal();
                User user= getUserFromDB(username);
//                if (user==null){
//                    throw new AuthenticationException("用户名错误");
//                }
                // 需要返回shiro规定的AuthenticationInfo类型的对象
                // 这个对象中，包含了用户的身份认证信息
                // SimpleAuthenticationInfo的构造函数中的第一个参数，principals代表用户的身份信息
                // 一般可以使用 user对象，或者使用用户名也可以
                // 第三个参数： 盐
                // 第四个参数，代表当前realm的名字，固定写法
                SimpleAuthenticationInfo authenticationInfo
                        = new SimpleAuthenticationInfo(user,user.getPassword(),
                        ByteSource.Util.bytes(user.getSalt()),this.getName());
                return authenticationInfo;
            }

            private User getUserFromDB(String username) {
//                User user = new User();
//                user.setId(4);
//                user.setUsername(username);
//                user.setPassword("8d07d6344cff223f900a729fca5d619e");
//                user.setSalt("2b36ae72");
                User dbuser = userService.getOne(new QueryWrapper<User>().eq("username", username));
                return dbuser;

            }

            // 获取授权信息
            @Override
            protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
                System.out.println("===============获取授权==============");

                // 从数据库表中查询当前用户具有的角色和权限字符串
                User user = (User) principals.getPrimaryPrincipal();
                List<String> roles = getRolesFromDB(user);
                List<String> permissions = getPermissionsFromDB(user);

                // 按照约定返回AuthorizationInfo对象
                SimpleAuthorizationInfo authorizationInfo = new SimpleAuthorizationInfo();
                // 放入从数据库中查询到的当前用户的角色信息
//                authorizationInfo.addRole("test");
//                authorizationInfo.addRole("admin");
//                authorizationInfo.addRoles(Arrays.asList("test","admin"));
                authorizationInfo.addRoles(roles);
                // 放入从数据库中查询到的当前用户的权限信息
//                authorizationInfo.addStringPermission("document:read");
//                authorizationInfo.addStringPermissions(Arrays.asList("document:read","document:write"));
                authorizationInfo.addStringPermissions(permissions);

                return authorizationInfo;
            }

            private List<String> getPermissionsFromDB(User user) {
//                return Arrays.asList("document:read","document:write");
                if (user.getPers()!=null){
                    return user.getPers();
                }
                List<String> permissions = userService.getPermissionsFromDB(user);
                user.setPers(permissions);
                return permissions;
            }

            private List<String> getRolesFromDB(User user) {
//                return Arrays.asList("test","admin");
                if (user.getRoles()!=null){
                    return user.getRoles();
                }
                List<String> roles = userService.getRolesFromDB(user);
                user.setRoles(roles);
                return roles;
            }
        };

        // 把HashedCredentialsMatcher对象设置到authorizingRealm对象中
        authorizingRealm.setCredentialsMatcher(hashedCredentialsMatcher());
        return authorizingRealm;
    }


    @Bean
    public HashedCredentialsMatcher hashedCredentialsMatcher(){

        HashedCredentialsMatcher matcher = new HashedCredentialsMatcher();

        // 设置算法名称
        matcher.setHashAlgorithmName("md5");

        // 设置hash次数
        matcher.setHashIterations(Constants.HASH_TIMES);

        return matcher;

    }

}
